#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

#include "h_lz77.h"

typedef unsigned char BYTE;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
typedef size_t SIZE_T;

#define ONCE_IO         2048       /* 每次读写的字节数 */
#define MIN_MATCH       3          /* 少于3个字节不压缩 */
#define MAX_MATCH       257        /* 最大匹配长度 */
#define WINDOW_SIZE     0x8000L    /* LZ77滑动窗口大小，32K */
#define HASH_SIZE       0x8000L    /* HASH表大小 */
#define HASH_MASK       0x7fffL    /* HASH屏蔽字 */
#define HASH_SHIFT      5          /* HASH每次移位bit数 */

struct HLz77Global
{
   BYTE *Window;             /* 保存最近读过的数据 */
   UINT32 WindowSize;        /* 窗口缓冲区大小 */
   SIZE_T WindowStart;       /* 窗口相对文件头的偏移 */
   UINT16 CurrOffset;        /* 当前处理的数据相对于窗口开始的偏移 */
   UINT16 CurrSize;          /* 当前处理的数据大小 */
   int Eof;                  /* 是否已经到文件尾 */
   FILE *fin;
   FILE *fout;
   const char *BufIn;
   char *BufOut;
   int BufInLen;
   int *BufOutLen;
   BYTE *BitsBuf;            /* bits流缓冲区 */
   int BitsBufDataSize;      /* bits流已经读入的数据 */
                             /*   （decompress时）*/
   int BitsBufPointer_byte;  /* bits缓冲区指针 */
   int BitsBufPointer_bit;   /* bits缓冲区指针（字节内）*/
   SIZE_T *HashHead;         /* hash表头 */
   UINT16 Hash;              /* 当前HASH值 */
};

/*
   流HASH算法，当前HASH值的计算通过上一HASH值累加本字节得到。
   只有保证 MIN_MATCH 次移位可以把旧数据完全移除才可行，
   即：HASH_SHIFT * MIN_MATCH >= 15 （因为HASH_MASK是15bit）
*/
#define UPDATE_HASH(Hash, c) Hash = ( ( Hash << HASH_SHIFT ) ^ c ) & HASH_MASK;

static int CompressInit(HLz77Global &g, FILE *a_fin, FILE *a_fout, const char *a_BufIn, char *a_BufOut, int a_BufInLen, int *a_BufOutLen)
{
   int i;

   if ( a_fin == NULL )
   {
      if ( a_fout != NULL )
      {
         return H_COMPRESS_ERR_OTHER;
      }
      if ( a_BufIn == NULL || a_BufOut == NULL || a_BufInLen == 0 || a_BufOutLen == NULL )
      {
         return H_COMPRESS_ERR_OTHER;
      }
   }
   else
   {
      if ( a_fout == NULL )
      {
         return H_COMPRESS_ERR_OTHER;
      }
      if ( a_BufIn != NULL || a_BufOut != NULL || a_BufInLen != 0 || a_BufOutLen != NULL )
      {
         return H_COMPRESS_ERR_OTHER;
      }
   }

   g.fin = a_fin;
   g.fout = a_fout;
   g.BufIn = a_BufIn;
   g.BufOut = a_BufOut;
   g.BufInLen = a_BufInLen;
   g.BufOutLen = a_BufOutLen;

   if ( g.BufOutLen != NULL )
   {
      *g.BufOutLen = 0;
   }

   g.Window = NULL;
   g.BitsBuf = NULL;
   g.WindowStart = 0;
   g.WindowSize = WINDOW_SIZE * 2;
   g.Window = (BYTE*)malloc(g.WindowSize);

   if ( g.Window == NULL )
   {
      return H_COMPRESS_ERR_NOMEM;
   }

   g.BitsBuf = (BYTE*)malloc(ONCE_IO);

   if ( g.BitsBuf == NULL )
   {
      return H_COMPRESS_ERR_NOMEM;
   }

   g.HashHead = (SIZE_T*)malloc(sizeof(SIZE_T) * HASH_SIZE);

   if ( g.HashHead == NULL )
   {
      return H_COMPRESS_ERR_NOMEM;
   }

   memset(g.HashHead, 0, sizeof(UINT16) * HASH_SIZE);
   g.BitsBufPointer_byte = 0;
   g.BitsBufPointer_bit = 0;
   g.BitsBuf[g.BitsBufPointer_byte] = 0;
   g.Hash = 0;
   return 0;
}

static void CompressClose(HLz77Global &g)
{
   if ( g.HashHead != NULL )
   {
      free(g.HashHead);
   }

   if ( g.BitsBuf != NULL )
   {
      free(g.BitsBuf);
   }

   if ( g.Window != NULL )
   {
      free(g.Window);
   }
}

int ReadOnce(HLz77Global &g)
{
/* 如果剩下的数据量不足 MAX_MATCH，窗口滑动 */
   if ( g.CurrOffset + MAX_MATCH > g.WindowSize )
   {
      memcpy(g.Window, g.Window + WINDOW_SIZE, WINDOW_SIZE);
      g.CurrOffset -= WINDOW_SIZE;
      g.WindowStart += WINDOW_SIZE;
   }

   if ( g.CurrSize >= ONCE_IO || g.Eof )
   {
      return 0;
   }

   int r;
   int Len = g.WindowSize - g.CurrOffset - g.CurrSize;

   if ( Len == 0 )
   {
      return 0;
   }

   if ( Len > ONCE_IO )
   {
      Len = ONCE_IO;
   }

   if ( g.fin == NULL )
   {
      if ( Len > g.BufInLen )
      {
         Len = g.BufInLen;
      }
      if ( Len > 0 )
      {
         memcpy(g.Window + g.CurrOffset + g.CurrSize, g.BufIn, Len);
         g.BufIn += Len;
         g.BufInLen -= Len;
         g.CurrSize += Len;
      }
   }
   else
   {
      Len = fread(g.Window + g.CurrOffset + g.CurrSize, 1, Len, g.fin);
      if ( Len <= 0 )
      {
         if ( feof(g.fin) )
         {
            g.Eof = 1;
         }
         else
         {
            return H_COMPRESS_ERR_STREAM;
         }
      }
      else
      {
         g.CurrSize += Len;
      }
   }

   return 0;
}

static int CompressFlush(HLz77Global &g)
{
   if ( g.BitsBufPointer_bit != 0 )
   {
      g.BitsBuf[g.BitsBufPointer_byte++] <<=
         8 - g.BitsBufPointer_bit;
   }

   if ( g.BitsBufPointer_byte == 0 )
   {
      return 0;
   }

   if ( g.fout == NULL )
   {
      if ( g.BitsBufPointer_byte > 0 )
      {
         memcpy(g.BufOut, g.BitsBuf, g.BitsBufPointer_byte);
         g.BufOut += g.BitsBufPointer_byte;
         *g.BufOutLen += g.BitsBufPointer_byte;
      }
   }
   else
   {
      if ( fwrite(g.BitsBuf, g.BitsBufPointer_byte, 1, g.fout) != 1 )
      {
         return H_COMPRESS_ERR_STREAM;
      }
   }

   return 0;
}

static int PutBits(HLz77Global &g, UINT16 Data, int n)
{
   int k;
   UINT16 i;

   Data <<= 16 - n;

   for ( ; n > 0; )
   {
      k = 8 - g.BitsBufPointer_bit;
      if ( k > n )
      {
         k = n;
      }
      i = g.BitsBuf[g.BitsBufPointer_byte] * 256 + Data / 256;
      i <<= k;
      g.BitsBuf[g.BitsBufPointer_byte] = i / 256;
      Data <<= k;
      n -= k;
      g.BitsBufPointer_bit += k;
      if ( g.BitsBufPointer_bit == 8 )
      {
         g.BitsBufPointer_bit = 0;
         g.BitsBufPointer_byte++;
         if ( g.BitsBufPointer_byte == ONCE_IO )
         {
            if ( g.fout == NULL )
            {
               memcpy(g.BufOut, g.BitsBuf, ONCE_IO);
               g.BufOut += ONCE_IO;
               *g.BufOutLen += ONCE_IO;
            }
            else
            {
               if ( fwrite(g.BitsBuf, ONCE_IO, 1, g.fout) != 1 )
               {
                  return H_COMPRESS_ERR_STREAM;
               }
            }
            g.BitsBufPointer_byte = 0;
         }
         g.BitsBuf[g.BitsBufPointer_byte] = 0;
      }
   }

   return 0;
}

#define COMPRESS_CHECK_ERR \
   if ( r != 0 ) \
   { \
      CompressClose(g); \
      return r; \
   }

static int MatchedLen(BYTE *Str, BYTE *Pattern, int MaxLen)
{
   int r;

   for ( r = 0; r < MaxLen && *Str == *Pattern;
         r++, Str++, Pattern++ )
      ;

   return r;
}

/*
   0  0 -- 7    3
   1  8 -- 23   4
   2 24 -- 55   5
   3 56 -- 255  8
*/

static int PutLength(HLz77Global &g, UINT16 Length)
{
   int r;

   Length -= 3;

   if ( Length < 8 )
   {
      r = PutBits(g, 4, 3);
      if ( r != 0 )
      {
         return r;
      }
      return PutBits(g, Length, 3);
   }

   if ( Length < 24 )
   {
      r = PutBits(g, 5, 3);
      if ( r != 0 )
      {
         return r;
      }
      return PutBits(g, Length - 8, 4);
   }

   if ( Length < 56 )
   {
      r = PutBits(g, 6, 3);
      if ( r != 0 )
      {
         return r;
      }
      return PutBits(g, Length - 24, 5);
   }

   r = PutBits(g, 7, 3);

   if ( r != 0 )
   {
      return r;
   }

   return PutBits(g, Length - 56, 8);
}

/*
   0   0  -- 63    6
   1  64  -- 575   9
   2 576  -- 4671  12
   3 4672 -- 32767 15
*/

static int PutDistance(HLz77Global &g, UINT16 Distance)
{
   int r;

   Distance--;

   if ( Distance < 64 )
   {
      r = PutBits(g, 0, 2);
      if ( r != 0 )
      {
         return r;
      }
      return PutBits(g, Distance, 6);
   }

   if ( Distance < 576 )
   {
      r = PutBits(g, 1, 2);
      if ( r != 0 )
      {
         return r;
      }
      return PutBits(g, Distance - 64, 9);
   }

   if ( Distance < 4672 )
   {
      r = PutBits(g, 2, 2);
      if ( r != 0 )
      {
         return r;
      }
      return PutBits(g, Distance - 576, 12);
   }

   r = PutBits(g, 3, 2);

   if ( r != 0 )
   {
      return r;
   }

   return PutBits(g, Distance - 4672, 15);
}

void AddHashItem(HLz77Global &g)
{
   g.HashHead[g.Hash] = g.WindowStart + g.CurrOffset;
}

int Lz77Compress(FILE *fin, FILE *fout, const char *BufIn, char *BufOut, int BufInLen, int *BufOutLen)
{
   int i;
   int Len;
   int r;
   BYTE Index;
   UINT32 Distance;
   UINT32 FoundOffset;
   int FoundLen;
   HLz77Global g;

   r = CompressInit(g, fin, fout, BufIn, BufOut, BufInLen, BufOutLen);
   COMPRESS_CHECK_ERR;
   g.CurrOffset = 0;
   g.CurrSize = 0;
   g.Eof = 0;

   for ( ; ; )
   {
      r = ReadOnce(g);
      COMPRESS_CHECK_ERR;
/* 最后剩下的几个字节做常量处理，不压缩 */
      if ( g.CurrSize < MIN_MATCH )
      {
         for ( ; g.CurrSize > 0; g.CurrSize--, g.CurrOffset++ )
         {
            r = PutBits(g, g.Window[g.CurrOffset], 9);
            COMPRESS_CHECK_ERR;
         }
         break;
      }
/* 字符串匹配 */
      if ( g.CurrOffset == 0 ) // 第一个字符串
      {
         for ( i = 0; i < MIN_MATCH; i++ )
         {
            UPDATE_HASH(g.Hash, g.Window[i]);
         }
         r = PutBits(g, g.Window[0], 9);
         COMPRESS_CHECK_ERR;
         g.CurrSize--;
         g.CurrOffset++;
         continue;
      }
      UPDATE_HASH(g.Hash, g.Window[g.CurrOffset + MIN_MATCH - 1]);
	  SIZE_T Offset = g.HashHead[g.Hash];
      FoundLen = 0;
      Distance = g.WindowStart + g.CurrOffset - Offset;
      if ( Distance <= WINDOW_SIZE && Distance < g.CurrOffset )
      {
         Len = g.CurrSize;
         if ( Len > Distance )
         {
            Len = Distance;
         }
         if ( Len > MAX_MATCH )
         {
            Len = MAX_MATCH;
         }
         FoundLen = MatchedLen(g.Window + g.CurrOffset - Distance, g.Window + g.CurrOffset, Len);
      }
      if ( FoundLen < 3 )
      {
         AddHashItem(g);
         r = PutBits(g, g.Window[g.CurrOffset], 9);
         COMPRESS_CHECK_ERR;
         g.CurrOffset++;
         g.CurrSize--;
         continue;
      }
      r = PutLength(g, FoundLen);
      COMPRESS_CHECK_ERR;
      r = PutDistance(g, Distance);
      COMPRESS_CHECK_ERR;
      AddHashItem(g);
      g.Hash = 0;
      g.CurrOffset += FoundLen;
      g.CurrSize -= FoundLen;
      for ( i = 0; i < MIN_MATCH - 1; i++ )
      {
         UPDATE_HASH(g.Hash, g.Window[g.CurrOffset + i]);
      }
   }

   r = PutBits(g, 7, 3);
   COMPRESS_CHECK_ERR;
   r = PutBits(g, MAX_MATCH + 1 - 56 - 3, 8);
   COMPRESS_CHECK_ERR;
   r = CompressFlush(g);
   COMPRESS_CHECK_ERR;
   CompressClose(g);
   return 0;
}

static int DecompressInit(HLz77Global &g, FILE *a_fin, FILE *a_fout, const char *a_BufIn, char *a_BufOut, int a_BufInLen, int *a_BufOutLen)
{
   int i;

   if ( a_fin == NULL )
   {
      if ( a_fout != NULL )
      {
         return H_COMPRESS_ERR_OTHER;
      }
      if ( a_BufIn == NULL || a_BufOut == NULL || a_BufInLen == 0 || a_BufOutLen == NULL )
      {
         return H_COMPRESS_ERR_OTHER;
      }
   }
   else
   {
      if ( a_fout == NULL )
      {
         return H_COMPRESS_ERR_OTHER;
      }
      if ( a_BufIn != NULL || a_BufOut != NULL || a_BufInLen != 0 || a_BufOutLen != NULL )
      {
         return H_COMPRESS_ERR_OTHER;
      }
   }

   g.fin = a_fin;
   g.fout = a_fout;
   g.BufIn = a_BufIn;
   g.BufOut = a_BufOut;
   g.BufInLen = a_BufInLen;
   g.BufOutLen = a_BufOutLen;

   if ( g.BufOutLen != NULL )
   {
      *g.BufOutLen = 0;
   }

   g.Window = NULL;
   g.BitsBuf = NULL;
   g.WindowSize = WINDOW_SIZE * 2;
   g.Window = (BYTE*)malloc(g.WindowSize);

   if ( g.Window == NULL )
   {
      return H_COMPRESS_ERR_NOMEM;
   }

   memset(g.Window, ' ', WINDOW_SIZE * 2);
   g.BitsBuf = (BYTE*)malloc(ONCE_IO);

   if ( g.BitsBuf == NULL )
   {
      return H_COMPRESS_ERR_NOMEM;
   }

   g.BitsBufPointer_byte = 0;
   g.BitsBufPointer_bit = 0;
   g.BitsBufDataSize = 0;
   return 0;
}

static void DecompressClose(HLz77Global &g)
{
   if ( g.BitsBuf != NULL )
   {
      free(g.BitsBuf);
   }

   if ( g.Window != NULL )
   {
      free(g.Window);
   }
}

static int GetBits(HLz77Global &g, UINT16 *Data, int n)
{
   int k;
   UINT16 i, j;

   for ( i = 0; n > 0 ; )
   {
      if ( g.BitsBufPointer_byte == g.BitsBufDataSize )
      {
         if ( g.Eof )
         {
            return H_COMPRESS_ERR_OTHER;
         }
         if ( g.fin == NULL )
         {
            if ( g.BufInLen > ONCE_IO )
            {
               g.BitsBufDataSize = ONCE_IO;
            }
            else
            {
               g.BitsBufDataSize = g.BufInLen;
            }
            if ( g.BitsBufDataSize > 0 )
            {
               memcpy(g.BitsBuf, g.BufIn, g.BitsBufDataSize);
               g.BufIn += g.BitsBufDataSize;
               g.BufInLen -= g.BitsBufDataSize;
            }
         }
         else
         {
            g.BitsBufDataSize = fread(g.BitsBuf, 1, ONCE_IO, g.fin);
         }
         if ( g.BitsBufDataSize < 0 )
         {
            return H_COMPRESS_ERR_STREAM;
         }
         if ( g.BitsBufDataSize < ONCE_IO )
         {
            g.Eof = 1;
         }
         g.BitsBufPointer_byte = 0;
         g.BitsBufPointer_bit = 0;
      }
      k = 8 - g.BitsBufPointer_bit;
      if ( k > n )
      {
         k = n;
      }
      j = ( g.BitsBuf[g.BitsBufPointer_byte] <<
            g.BitsBufPointer_bit ) % 256;
      j <<= k;
      i = ( i << k ) + j / 256;
      n -= k;
      g.BitsBufPointer_bit += k;
      if ( g.BitsBufPointer_bit == 8 )
      {
         g.BitsBufPointer_bit = 0;
         g.BitsBufPointer_byte++;
      }
   }

   *Data = i;
   return 0;
}

static int DecompressOutput(HLz77Global &g, BYTE Data)
{
   if ( (UINT32)g.CurrOffset + 1 >= g.WindowSize )
   {
      memcpy(g.Window, g.Window + WINDOW_SIZE, WINDOW_SIZE);
      g.CurrOffset -= WINDOW_SIZE;
   }

   g.Window[g.CurrOffset++] = Data;
   g.CurrSize++;

   if ( g.CurrSize >= ONCE_IO )
   {
      if ( g.fout == NULL )
      {
         memcpy(g.BufOut, g.Window + g.CurrOffset - g.CurrSize, g.CurrSize);
         g.BufOut += g.CurrSize;
         *g.BufOutLen += g.CurrSize;
      }
      else
      {
         if ( fwrite(g.Window + g.CurrOffset - g.CurrSize,
                 g.CurrSize, 1, g.fout) != 1 )
         {
            return H_COMPRESS_ERR_STREAM;
         }
      }
      g.CurrSize = 0;
   }

   return 0;
}

static int DecompressOutputLength(HLz77Global &g, BYTE *Data, int Length)
{
   if ( (UINT32)g.CurrOffset + Length >= g.WindowSize )
   {
      memcpy(g.Window, g.Window + WINDOW_SIZE, WINDOW_SIZE);
      memset(g.Window + WINDOW_SIZE, ' ', WINDOW_SIZE);
      g.CurrOffset -= WINDOW_SIZE;
      Data -= WINDOW_SIZE;
   }

   memcpy(g.Window + g.CurrOffset, Data, Length);
   g.CurrOffset += Length;
   g.CurrSize += Length;

   if ( g.CurrSize >= ONCE_IO )
   {
      if ( g.fout == NULL )
      {
         memcpy(g.BufOut, g.Window + g.CurrOffset - g.CurrSize, g.CurrSize);
         g.BufOut += g.CurrSize;
         *g.BufOutLen += g.CurrSize;
      }
      else
      {
         if ( fwrite(g.Window + g.CurrOffset - g.CurrSize,
                 g.CurrSize, 1, g.fout) != 1 )
         {
            return H_COMPRESS_ERR_STREAM;
         }
      }
      g.CurrSize = 0;
   }

   return 0;
}

static int DecompressFlush(HLz77Global &g)
{
   if ( g.CurrSize == 0 )
   {
      return 0;
   }

   if ( g.fout == NULL )
   {     
      if ( g.CurrSize > 0 )
      {
         memcpy(g.BufOut, g.Window + g.CurrOffset - g.CurrSize, g.CurrSize);
         g.BufOut += g.CurrSize;
         *g.BufOutLen += g.CurrSize;
      }
   }     
   else  
   {     
      if ( fwrite(g.Window + g.CurrOffset - g.CurrSize,
             g.CurrSize, 1, g.fout) != 1 )
      {
         return H_COMPRESS_ERR_STREAM;
      }
   }

   return 0;
}

int GetLength(HLz77Global &g, UINT16 *Length)
{
   int r;
   UINT16 i, j;

   r = GetBits(g, &i, 2);

   if ( r != 0 )
   {
      return r;
   }

   switch ( i )
   {
   case 0 :
      r = GetBits(g, &j, 3);
      if ( r != 0 )
      {
         return r;
      }
      *Length = j + 3;
      break;
   case 1 :
      r = GetBits(g, &j, 4);
      if ( r != 0 )
      {
         return r;
      }
      *Length = j + 8 + 3;
      break;
   case 2 :
      r = GetBits(g, &j, 5);
      if ( r != 0 )
      {
         return r;
      }
      *Length = j + 24 + 3;
      break;
   default :
      r = GetBits(g, &j, 8);
      if ( r != 0 )
      {
         return r;
      }
      *Length = j + 56 + 3;
      break;
   }

   return 0;
}

int GetDistance(HLz77Global &g, UINT16 *Distance)
{
   int r;
   UINT16 i, j;

   r = GetBits(g, &i, 2);

   if ( r != 0 )
   {
      return r;
   }

   switch ( i )
   {
   case 0 :
      r = GetBits(g, &j, 6);
      if ( r != 0 )
      {
         return r;
      }
      *Distance = j + 1;
      break;
   case 1 :
      r = GetBits(g, &j, 9);
      if ( r != 0 )
      {
         return r;
      }
      *Distance = j + 64 + 1;
      break;
   case 2 :
      r = GetBits(g, &j, 12);
      if ( r != 0 )
      {
         return r;
      }
      *Distance = j + 576 + 1;
      break;
   default :
      r = GetBits(g, &j, 15);
      if ( r != 0 )
      {
         return r;
      }
      *Distance = j + 4672 + 1;
      break;
   }

   return 0;
}

#define DECOMPRESS_CHECK_ERR \
   if ( r != 0 ) \
   { \
      DecompressClose(g); \
      return r; \
   }

int Lz77Decompress(FILE *fin, FILE *fout, const char *BufIn, char *BufOut, int BufInLen, int *BufOutLen)
{
   int r;
   UINT16 i;
   UINT16 Length;
   UINT16 Distance;
   HLz77Global g;

   r = DecompressInit(g, fin, fout, BufIn, BufOut, BufInLen, BufOutLen);
   DECOMPRESS_CHECK_ERR;

   g.CurrOffset = 0;
   g.CurrSize = 0;
   g.Eof = 0;

   for ( ; ; )
   {
      r = GetBits(g, &i, 1);
      DECOMPRESS_CHECK_ERR;
      if ( i == 0 ) /* 常量字符 */
      {
         r = GetBits(g, &i, 8);
         DECOMPRESS_CHECK_ERR;
         r = DecompressOutput(g, i);
         DECOMPRESS_CHECK_ERR;
         continue;
      }
      r = GetLength(g, &Length);
      DECOMPRESS_CHECK_ERR;
      if ( Length == MAX_MATCH + 1 )
      {
         break;
      }
      r = GetDistance(g, &Distance);
      DECOMPRESS_CHECK_ERR;
      if ( Distance > g.CurrOffset )
      {
         return H_COMPRESS_ERR_OTHER;
      }
      for ( ; Length > 0; )
      {
         int Len = Length;
         if ( Len > Distance )
         {
            Len = Distance;
         }
         r = DecompressOutputLength(g, g.Window + g.CurrOffset - Distance, Len);
         DECOMPRESS_CHECK_ERR;
         Length -= Len;
         Distance += Len;
      }
   }

   r = DecompressFlush(g);
   DECOMPRESS_CHECK_ERR;
   DecompressClose(g);
   return 0;
}

int Lz77CompressFile(FILE *fin, FILE *fout)
{
   return Lz77Compress(fin, fout, NULL, NULL, 0, NULL);
}

int Lz77DecompressFile(FILE *fin, FILE *fout)
{
   return Lz77Decompress(fin, fout, NULL, NULL, 0, NULL);
}

int Lz77CompressBuf(const char *BufIn, char *BufOut, int BufInLen, int *BufOutLen)
{
   return Lz77Compress(NULL, NULL, BufIn, BufOut, BufInLen, BufOutLen);
}

int Lz77DecompressBuf(const char *BufIn, char *BufOut, int BufInLen, int *BufOutLen)
{
   return Lz77Decompress(NULL, NULL, BufIn, BufOut, BufInLen, BufOutLen);
}
